package red.stu.window.editor.code;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.LinkedList;

import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;

public class CodeEditor extends JTextPane implements CaretListener, KeyListener {

	private static final int RR_MAX_SIZE = 128; // 最大撤回缓存代码数量
	private static final int RR_CLEAN_SIZE = 50; // 每次清理数量
	
	private RevocationRecord revocationRecord = new RevocationRecord(RR_MAX_SIZE, RR_CLEAN_SIZE);
	// JScrollPane
	private JScrollPane scrollPane;
	private DefaultStyledDocument doc;
	// 当前的高亮词库
	private HighlighterCarver carver;
	
	private Callback callback;
	
	public CodeEditor(Callback callback) {
		super();
		this.callback = callback;
		
		super.setFont(new Font("Consolas", 0, 18));

		scrollPane = new JScrollPane(this);

		StyleContext context = new StyleContext();
		doc = new DefaultStyledDocument(context);
		super.setDocument(doc);

		SimpleAttributeSet numberStyle = DefaultLexicon.DEFAULT_STYLE_NUMBER;

		SimpleAttributeSet stringStyle = new SimpleAttributeSet();
		StyleConstants.setForeground(stringStyle, Color.BLUE);

		SimpleAttributeSet annotationSingleStyle = new SimpleAttributeSet();
		StyleConstants.setForeground(annotationSingleStyle, Color.GREEN);

		SimpleAttributeSet annotationMultiStyle = new SimpleAttributeSet();
		StyleConstants.setForeground(annotationMultiStyle, Color.GREEN);

		SimpleAttributeSet otherStyle = DefaultLexicon.DEFAULT_STYLE_OTHER;

		carver = new HighlighterCarver(doc, DefaultLexicon.JSLexicon, numberStyle, stringStyle, annotationSingleStyle,
				annotationMultiStyle, otherStyle);
		/*
		 * keyAttr = new SimpleAttributeSet(); StyleConstants.setBold(keyAttr, true);
		 * StyleConstants.setForeground(keyAttr, Color.RED);
		 * 
		 * normalAttr = new SimpleAttributeSet(); //
		 * StyleConstants.setFontFamily(normalAttr, "serif");
		 * StyleConstants.setBold(normalAttr, false);
		 * StyleConstants.setForeground(normalAttr, Color.black);
		 * 
		 * bracketAttr = new SimpleAttributeSet();
		 * StyleConstants.setForeground(bracketAttr, Color.RED); //
		 * StyleConstants.setFontFamily(bracketAttr, "serif");
		 * StyleConstants.setBold(bracketAttr, true);
		 */

		super.addCaretListener(this); // 字符串选择监听
		super.addKeyListener(this); // 按键监听
		// textPane.addAncestorListener(listener);
		// textPane.addComponentListener(this); // 窗口改变事件监听
		// textPane.addContainerListener(l); // 组件内容添加监听
		// textPane.addFocusListener(l); // 焦点 获取/失去事件监听
		// textPane.addHierarchyBoundsListener(l); // 层次边框监听
		// textPane.addHierarchyListener(l); // 层次监听
		// textPane.addHyperlinkListener(listener); // 超链接监听
		// textPane.addInputMethodListener(l); // 输入方法监听(一般源至输入法)
		// textPane.addMouseListener(l); // 鼠标监听
		// textPane.addMouseMotionListener(l); // 鼠标移动监听
		// textPane.addVetoableChangeListener(this);

		setText("function test() { if ('ac' == 123) {return 'ok';} }");

		onTextUpdate();
	}

	public JScrollPane getScrollPane() {
		return scrollPane;
	}

	public void onTextUpdate() {
		String code = this.getText();
		carver.draw(code + " ");
	}

	// 字符选择更新更新
	@Override
	public void caretUpdate(CaretEvent arg0) {
		// System.out.println("addCaretListener:" + arg0);
	}

	// 按键按下
	@Override
	public void keyPressed(KeyEvent e) {
		// System.out.println("keyPressed: " + e);
		if (e.getKeyCode() == 27) {
			callback.onEscKeyPressed();
		}
	}

	// 按键释放
	@Override
	public void keyReleased(KeyEvent e) {
		int keyCode = e.getKeyCode();
		int keyModifiers = e.getModifiers();
		String code = this.getText();
		int cCode = (int) e.getKeyChar();
		if (keyModifiers == 0 && cCode != 65535) {
			revocationRecord.add(code);
		} else if (keyModifiers == 2) { // Ctrl
			if (keyCode == 90) { // z
				this.setText(revocationRecord.back());
			} else if (keyCode == 89) { // y
				this.setText(revocationRecord.restore());
			}
		}
		
		
		int cpos = super.getCaretPosition();
		if (cpos == 0)
			return;
		char lastChar = ' ';
		char keyChar = e.getKeyChar();

		try {
			lastChar = doc.getText(cpos - 1, 1).charAt(0);
		} catch (BadLocationException e2) {
		}

		try {
			switch (keyChar) {
			case '\n':
				String str = findEndString(doc.getText(0, cpos - 1), "\n");
				if (str != null) {
					int tabNum = findTabCharNumber(str);
					Character lastCharcter = findLastChar(str);
					if (lastCharcter != null && lastCharcter.charValue() == '{')
						tabNum++;
					doc.insertString(cpos, repetitionString("\t", tabNum), null);
				}
				break;
				
			case '(':
					doc.insertString(cpos, ")", null);
					super.setCaretPosition(cpos);
				break;

			default:
				break;
			}
		} catch (BadLocationException e1) {
		}

		// System.out.println("keyReleased: " + e);
		onTextUpdate();
	}
	
	private static String findEndString(String text, String s) {
		String[] strs = text.split(s);
		if (strs.length == 0)
			return null;
		return strs[strs.length - 1];
	}
	
	private static int findTabCharNumber(String text) {
		char[] cs = text.toCharArray();
		int num = 0;
		for (char c : cs) {
			if (c != '\t')
				break;
			num++;
		}
		return num;
	}
	
	private static Character findLastChar(String text) {
		text = text.trim();
		if (text.length() == 0)
			return null;
		return text.charAt(text.length() - 1);
	}
	
	private static String repetitionString(String str, int num) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < num; i++) {
			sb.append(str);
		}
		return sb.toString();
	}

	// 按键类型
	@Override
	public void keyTyped(KeyEvent e) {
		// System.out.println("keyTyped: " + e);
	}
	
	public interface Callback {
		
		void onEscKeyPressed();
		
	}

}

class RevocationRecord {
	
	private LinkedList<String> cacheCode = new LinkedList<>();
	private String lastCode = null;
	private int maxSize;
	private int cleanSize;
	private int index;
	
	RevocationRecord(int maxSize, int cleanSize) {
		this.maxSize = maxSize;
		this.cleanSize = cleanSize;
		this.index = 0;
	}
	
	void add(String code) {
		if (code == null || code.equals(lastCode))
			return;
		lastCode = code;
		for (int i = cacheCode.size() - 1; i > index; i--) {
			cacheCode.remove(i);
		}
		if (cacheCode.size() == maxSize) {
			for (int i = 0; i < cleanSize; i++) {
				cacheCode.removeFirst();
			}
		}
		cacheCode.add(code);
		index = cacheCode.size() - 1;
	}
	
	String back() {
		if (--index <= 0)
			index = 0;
		return cacheCode.get(index);
	}
	
	String restore() {
		if (++index >= cacheCode.size())
			index--;
		return cacheCode.get(index);
	}
	
}
